
#include "NetworkDBFromXML.h"

/** Constructor for the class, reads and parses in the XML file.
 * 
 * @param ifilename
 * The filename of the XML file containing the network data.
 * 
 * @param imp
 * The parent memory pool from which to generate the memory pool for the class. */
NetworkDBFromXML::NetworkDBFromXML(const char *ifilename, apr_pool_t *imp) : pool(imp)
{
   netxml = new APRDOMDocument(ifilename, pool.mp());
}

/** Destructor for the class. */
NetworkDBFromXML::~NetworkDBFromXML()
{
   delete netxml;
}

/** Merges this read XML data with the given network database.
 * 
 * @param netdb
 * The network database pointer.
 * 
 * @return Zero on success, non-zero on failure. */
int NetworkDBFromXML::mergeXMLWithNetworkDB(NetworkDB *netdb)
{
   apr_xml_elem *rootnode = netxml->getRootNode();
   if (rootnode == NULL)
   {
      errprint("could not merge XML with database: no root node");
      return 1;
   }
   if (strcmp(rootnode->name, "clusters") != 0)
   {
      errprint("warning: root node of clusters XML file not 'clusters'");
   }
   apr_xml_elem *this_elem = rootnode->first_child;
   parseTree(this_elem, netdb, NULL);
   return 0;
}

/** Parse the DOM tree into calls to add data to the database.
 * 
 * @param elem
 * The top-level element in this sub-part of the XML tree.
 * 
 * @param netdb
 * The network database pointer.
 * 
 * @param active_cluster
 * If we are inside a cluster element, what is the cluster ID
 * of that element, so that we can know which cluster owns this data?
 * 
 * @return Zero on success, non-zero on failure. */
int NetworkDBFromXML::parseTree(apr_xml_elem *elem, NetworkDB *netdb, const char *active_cluster)
{
   while (elem != NULL)
   {
      if (strcmp(elem->name, "cluster") == 0)
      {
         const char *subtree_active_cluster = NULL;
         subtree_active_cluster = parseCluster(elem, netdb);
         parseTree(elem->first_child, netdb, subtree_active_cluster);
      }
      if (strcmp(elem->name, "host") == 0)
         parseHost(elem, netdb, active_cluster);
      if (strcmp(elem->name, "alias") == 0)
         parseAlias(elem, netdb);
      elem = elem->next;
   }
   return 0;
}

/** Parse a cluster tag into cluster data, returning the cluster ID of the
 * cluster that was read.
 * 
 * @param elem
 * The cluster element in this sub-part of the XML tree.
 * 
 * @param netdb
 * The network database pointer.
 * 
 * @return The name of the cluster (cluster ID). */
const char *NetworkDBFromXML::parseCluster(apr_xml_elem *elem, NetworkDB *netdb)
{
   const char *read_clusterid = NULL;
   const char *read_clusterhome = NULL;
   apr_xml_attr *this_attr = elem->attr;
   while (this_attr != NULL)
   {
      if (strcmp(this_attr->name, "id") == 0)
         read_clusterid = this_attr->value;
      if (strcmp(this_attr->name, "workingdir") == 0)
         read_clusterhome = this_attr->value;
      this_attr = this_attr->next;
   }
   if ((read_clusterid) && (read_clusterhome))
   {
      netdb->addCluster(read_clusterid, read_clusterhome);
   }
   return read_clusterid;
}

/** Parse a host tag into host data, which belongs to the
 * given cluster ID (making it a headnode) of the cluster that was read.
 * 
 * @param elem
 * The cluster element in this sub-part of the XML tree.
 * 
 * @param netdb
 * The network database pointer.
 * 
 * @param clusterid
 * The cluster ID that this nodes belongs to.
 * 
 * @return Zero on success, non-zero on failure. */
int NetworkDBFromXML::parseHost(apr_xml_elem *elem, NetworkDB *netdb, const char *clusterid)
{
   const char *read_hostname = NULL;
   const char *read_hostport = NULL;
   const char *read_fingerprint = NULL;
   apr_xml_attr *this_attr = elem->attr;
   while (this_attr != NULL)
   {
      if (strcmp(this_attr->name, "hostname") == 0)
         read_hostname = this_attr->value;
      if (strcmp(this_attr->name, "sshport") == 0)
         read_hostport = this_attr->value;
      if (strcmp(this_attr->name, "fingerprint") == 0)
         read_fingerprint = this_attr->value;
      this_attr = this_attr->next;
   }
   if (read_hostname)
   {
      if (read_hostport == NULL)
         errprint("warning: defaulting to port 22 for SSH on host '%s'", read_hostname);
      netdb->addHost(read_hostname, (read_hostport) ? atoi(read_hostport) : 22, read_fingerprint, clusterid);
   }
   return 0;
}

/** Parse a host name alias from the network XML data.
 * 
 * @param elem
 * The host name alias element in this sub-part of the XML tree.
 * 
 * @param netdb
 * The network database pointer.
 * 
 * @return Zero on success, non-zero on failure. */
int NetworkDBFromXML::parseAlias(apr_xml_elem *elem, NetworkDB *netdb)
{
   const char *read_hostname = NULL;
   const char *read_alias = NULL;
   apr_xml_attr *this_attr = elem->attr;
   while (this_attr != NULL)
   {
      if (strcmp(this_attr->name, "hostname") == 0)
         read_hostname = this_attr->value;
      if (strcmp(this_attr->name, "aliasname") == 0)
         read_alias = this_attr->value;
      this_attr = this_attr->next;
   }
   if ((read_hostname) && (read_alias))
   {
      netdb->addAlias(read_alias, read_hostname);
   }
   return 0;
}
